#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <curses.h>
#include <limits.h>
#define UP		1
#define DOWN	-1
#define LEFT	2
#define RIGHT	-2
#define DEFAULT_WIDTH 20
#define DEFAULT_HEIGHT 20
#define SCORE_FILE "scores.txt"
int mapWidth = DEFAULT_HEIGHT;
int mapHight = DEFAULT_HEIGHT;
int score = 0;           // 用于记录分数
int gameRunning = 1;     // 控制游戏运行状态
int snakeSpeed = 250000; // 初始速度为250毫秒
int highScore = 0; 
struct Snake{
		int hang;
		int lie;
		struct Snake* next;
};
struct Snake* Head = NULL;
struct Snake* Tail = NULL;
struct Snake Food;
int dir = 0;
int key = 0;
//加载最高分数
void loadHighScore()
{
    FILE *file = fopen(SCORE_FILE, "r");
    if (file == NULL)
    {
		file = fopen(SCORE_FILE,"w");
        fwrite(&highScore,sizeof(int),1,file);
       
    }
	fseek(file,0,SEEK_SET);
	fread(&highScore,sizeof(int),1,file);
	fclose(file);
	file = NULL;
}
//保存并对比历史最高分数
void saveScore()
{
    FILE *file = fopen(SCORE_FILE, "w");
    if (file != NULL)
    {
        fwrite(&score,sizeof(int),1,file);
        fclose(file);
		file = NULL;
    }else{
		perror("ERROR OPENING FILE");
	}
	
}

//Ncurse 图形界面初始化配置
void initNcurse(void)
{
	initscr();
	keypad(stdscr,1);
	noecho();
}
//初始化食物位置范围控制在地图空间内
void initFood()
{
	int x = rand()%(mapWidth-2)+1;
	int y = rand()%(mapHight-2)+1;
	Food.hang = x;
	Food.lie = y;
	
}
//判断是否是蛇身的节点是的话创建蛇节点
int HaveSnakeNode(int i,int j)
{
	struct Snake* p = Head;
	while(p != NULL)
	{
		if(p->hang == i && p->lie == j)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;
}
//判断是否是食物的节点是的话创建食物
int HaveFood(int i,int j)
{
		if(Food.hang == i && Food.lie == j)
		{
			return 1;
		}
		return 0;
		
}
//循环遍历行和列把地图和蛇身以及食物展开
void SnakeMap()
{
	int hang,lie;
	move(0,0);
	for(hang = 0;hang < mapHight;hang++)
	{
		if(hang == 0)
		{
			for(lie = 0;lie < mapWidth;lie++)
			{
				printw("--");
			}
			printw("\n");
		}
		if(hang >= 0 && hang <= mapHight)
		{
			for(lie = 0;lie <= mapWidth;lie++)
			{
				if(lie == 0 || lie == mapWidth)
				{
					printw("|");
				}
				else if(HaveSnakeNode(hang,lie))
				{
					printw("[]");
				}
				else if(HaveFood(hang,lie)){
					printw("**");
				}
				else{
					printw("  ");
				}
			}
			printw("\n");
		}
		if( hang == mapHight-1)
		{
			for(lie = 0;lie < mapWidth;lie++)
			{
				printw("--");
			}
			printw("\n");
			printw("   By MaxLiang Scores = %d\n",score);
		}
	}
}
//添加蛇身的节点当收到键盘的方向键按下时对应的链表中的新节点变换为对应方向的位置
void addSnakeNode()
{
	struct Snake* new = (struct Snake*)malloc(sizeof(struct Snake));
	
	
	switch(key){
		case UP:
			new->hang = Tail->hang-1;
			new->lie = Tail->lie;
			break;
		case DOWN:
			new->hang = Tail->hang+1;
			new->lie = Tail->lie;
			break;
		case LEFT:
			new->hang = Tail->hang;
			new->lie = Tail->lie-1;
			break;
		case RIGHT:
			new->hang = Tail->hang;
			new->lie = Tail->lie+1;
			break;
		
	}
	new->next = NULL;
	Tail->next = new;
	Tail = new;
}
//初始化蛇本身并向右行走
void initSnake()
{
	while(Head != NULL)
	{
		struct Snake* p = Head;
		Head = Head->next;
		free(p);
	}
	key = RIGHT;
	initFood();
	Head = (struct Snake*)malloc(sizeof(struct Snake));
	Head->hang = 3;
	Head->lie = 2;
	Head->next = NULL;
	
	Tail = Head;
	addSnakeNode();
	addSnakeNode();
}
//删除蛇的节点
void deleteNode()
{
	struct Snake* p = Head;
	Head = Head->next;
	
	free(p);
}
//判断蛇是否吃到自己或者是否碰到了墙壁
int ifSnakeDie(){
	struct Snake*p = Head;
	if(Tail->hang < 0 || Tail->lie == 0 || Tail->hang == mapHight || Tail->lie == mapWidth)
	{
		return 1;
	}
	while(p->next != NULL)
	{
		if(p->hang == Tail->hang && p->lie == Tail->lie)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;
}
void updateSpeed()
{
    if (score >= 50 && score < 100)
    {
        snakeSpeed = 250000 / 1.5; // 50分时速度变为1.5倍
    }
    else if (score >= 100 && score < 150)
    {
        snakeSpeed = 250000 / 2; // 100分时速度变为2倍
    }
    else if (score >= 150 && score < 200)
    {
        snakeSpeed = 250000 / 2.5; // 150分时速度变为2.5倍
    }
	else if (score >= 200 && score < 250)
    {
        snakeSpeed = 250000 / 3; // 150分时速度变为2.5倍
    }
	else if (score >= 250)
    {
        snakeSpeed = 250000 / 3.5; // 150分时速度变为2.5倍
    }
}

//移动蛇身并且如果吃到了食物就重新初始化食物同样的如果蛇的失败条件满足则重新初始化蛇身
void moveSnakeR()
{
	addSnakeNode();
	if(HaveFood(Tail->hang,Tail->lie)){
		initFood();
		updateSpeed();
		score += 10;
	}else{
		deleteNode();
	}
	if(ifSnakeDie())
	{
		//initSnake();
		gameRunning = 0;
	}
}
//刷新地图同时分线程接收；线程一执行刷新地图和蛇身游走的效果
void* RefreshMap(){
	
	while(1)
	{
			moveSnakeR();
			SnakeMap();
			refresh();
			usleep(snakeSpeed);
	}
	
}
//约束键盘输入在转向时不可以即向左又向右走的条件
void turn(int dir2)
{
	if(abs(key) != abs(dir2)){
		key = dir2;
	}
}
//线程二执行接收键盘上的按键方向值同时改变按键值控制蛇的游走
void* ChangeDir()
{
	while(1)
	{
		dir = getch();
		switch(dir)
		{
			case KEY_UP:
				turn(UP);
				break;
			case KEY_DOWN:
				turn(DOWN);;
				break;
			case KEY_LEFT:
				turn(LEFT);
				break;
			case KEY_RIGHT:
				turn(RIGHT);
				break;
			
		}
		
	}
}
void* GameTimer() {
    sleep(120); // 等待二分钟
    gameRunning = 0; // 结束游戏
    return NULL;
}
void printGameOver()
{
	printf("Game Over ! score = %d\n",score);
	if (score > highScore)
		{
			printf("恭喜你打破最高记录，最高记录更新为%d\n", score);
			saveScore();
		}
		else if (score == highScore)
		{
			printf("与最高记录持平，加油再创佳绩\n");
		}
		else
		{
			printf("请继续加油，当前与最高记录相差%d\n", highScore - score);
		}
	printf("Author is by MaxLiang\n");
}

int main(int argc, char* argv[])
{    
	printf("请输入地图的大小:");
	scanf("%d %d",&mapWidth,&mapHight);
	pthread_t th1;
	pthread_t th2;
	pthread_t th3;
	
	initNcurse();
	loadHighScore();
	initSnake();
	SnakeMap();
	
	pthread_create(&th1, NULL, RefreshMap, NULL);
	pthread_create(&th2, NULL, ChangeDir, NULL);
	pthread_create(&th3, NULL, GameTimer, NULL);
	
	while(gameRunning);
	endwin();
	system("clear");
	printGameOver();
	return 0;
}
